#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sharp-keylist.h>

/*
struct sharp_keylist_s {
    char *key;
    void *data;
    sharp_keylist *next;
};
*/

static bool createNode(sharp_keylist **node, char *key){
    sharp_keylist *tmpNode;

    *node = tmpNode = (sharp_keylist*)malloc(sizeof(sharp_keylist));
    if(tmpNode == NULL)
        return false;
    tmpNode->data = NULL;
    tmpNode->key = key;
    tmpNode->next = NULL;
    return true;
}

bool sharp_keylist_init(sharp_keylist **list){
    *list = NULL;
    return true;
}

bool sharp_keylist_insert(sharp_keylist **list, char *key, void *data){
    sharp_keylist *tmpNode, *tmpList, *prevNode = NULL, *nextNode;
    int comparison;

    if(!key)
        return false;
    tmpList = *list;
    if(!tmpList){
        if(!createNode(&tmpNode, key))
            return false;
        tmpNode->data = data;
        *list = tmpNode;
        return true;
    }
    for(nextNode = NULL; sharp_keylist_iter(tmpList, &nextNode); ){
        if((comparison = strcasecmp(key, nextNode->key)) < 0){
            prevNode = nextNode;
            continue;
        }else if(!comparison){
            nextNode->data = data;/* overwrite */
            return true;
        }else{
            if(!createNode(&tmpNode, key))
                return false;
            tmpNode->next = nextNode;
            tmpNode->data = data;
            if(!prevNode){
                *list = tmpNode;
                return true;
            }
            prevNode->next = tmpNode;
            return true;
        }
    }
    if(!createNode(&tmpNode, key))
        return false;
    if(!prevNode){
        tmpNode->data = data;
        *list = tmpNode;
        return true;
    }
    tmpNode->data = data;
    prevNode->next = tmpNode;
    return true;
}

bool sharp_keylist_remove(sharp_keylist **list, char *key, void **data){
    sharp_keylist *tmpList, *prevNode = NULL, *nextNode;
    int comparison;

    tmpList = *list;
    for(nextNode = NULL; sharp_keylist_iter(tmpList, &nextNode); ){
        if((comparison = strcasecmp(key, nextNode->key)) < 0){
            prevNode = nextNode;
            continue;
        }else if(!comparison){
            *data = nextNode->data;
            if(!prevNode)
                *list = nextNode;
            else
                prevNode->next = nextNode;
            return true;
        }else{
            *data = NULL;
            return false;
        }        
    }
    *data = NULL;
    return false;
}

bool sharp_keylist_move(sharp_keylist **list, char *key, char *new_key){
    void *tmpData;

    if(key == new_key)
        return true;
    if(!sharp_keylist_remove(list, key, &tmpData))
        return false;
    return sharp_keylist_insert(list, new_key, tmpData);
}

bool sharp_keylist_get(sharp_keylist *list, char *key, void **data){
    sharp_keylist *nextNode;

    for(nextNode = NULL; sharp_keylist_iter(list, &nextNode); ){
        if(!strcasecmp(key, nextNode->key)){
            *data = nextNode->data;
            return true;
        }
    }
    *data = NULL;
    return false;
}

bool sharp_keylist_get_manual(sharp_keylist *list, char **key, void **data,
        bool (*matches_data)(void *data, void *withdata), void *withdata){
    sharp_keylist *nextNode;

    for(nextNode = NULL; sharp_keylist_iter(list, &nextNode); ){
        if(matches_data(nextNode->data, withdata)){
            *data = nextNode->data;
            return true;
        }
    }
    *data = NULL;
    return false;
}

bool sharp_keylist_iter(sharp_keylist *list, sharp_keylist **next){
    sharp_keylist *tmpList;

    tmpList = *next;
    if(tmpList == NULL){
        *next = list;
        return true;
    }
    if((*next = tmpList->next) == NULL)
        return false;
    return true;
}

bool sharp_keylist_isempty(sharp_keylist *list){
    return (list == NULL) ? true : false;
}
